home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / GDebi / GDebi.py < prev    next >
Encoding:
Python Source  |  2009-04-01  |  29.7 KB  |  729 lines

  1. # Copyright (c) 2005-2009 Canonical Ltd
  2. #
  3. # AUTHOR:
  4. # Michael Vogt <mvo@ubuntu.com>
  5. #
  6. # This file is part of GDebi
  7. #
  8. # GDebi is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License as published
  10. # by the Free Software Foundation; either version 2 of the License, or (at
  11. # your option) any later version.
  12. #
  13. # GDebi is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. # General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with GDebi; if not, write to the Free Software
  20. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21. #
  22.  
  23.  
  24. import sys
  25. import os
  26. import string
  27. import warnings
  28. from warnings import warn
  29. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  30. import apt
  31. import apt_pkg
  32.  
  33. import pygtk
  34. pygtk.require("2.0")
  35. import gtk, gtk.glade
  36. import pango
  37. import gobject
  38. import vte
  39. import urllib
  40. import fcntl
  41. import posix
  42. import time
  43. import thread
  44. import re
  45.  
  46. from DebPackage import DebPackage, Cache
  47. from SimpleGladeApp import SimpleGladeApp
  48. from apt.progress import InstallProgress
  49. from GDebiCommon import GDebiCommon, utf8
  50. from gettext import gettext as _
  51.  
  52. # the timeout when the termial is expanded if no activity from dpkg
  53. # is happening 
  54. GDEBI_TERMINAL_TIMEOUT=4*60.0
  55.  
  56. class GDebi(SimpleGladeApp, GDebiCommon):
  57.  
  58.     def __init__(self, datadir, options, file=""):
  59.         GDebiCommon.__init__(self,datadir,options,file)
  60.         localesApp="gdebi"
  61.         localesDir="/usr/share/locale"
  62.         gtk.glade.bindtextdomain(localesApp, localesDir)
  63.         gtk.glade.textdomain(localesApp)
  64.  
  65.         SimpleGladeApp.__init__(self, domain="gdebi",
  66.                                 path=datadir+"/gdebi.glade")
  67.         # use a nicer default icon
  68.         icons = gtk.icon_theme_get_default()
  69.         try:
  70.           logo=icons.load_icon("gnome-mime-application-x-deb", 48, 0)
  71.           if logo != "":
  72.             gtk.window_set_default_icon_list(logo)
  73.         except Exception, e:
  74.           print "Error loading logo"
  75.           pass
  76.   
  77.         # set image of button "install"  manually, since it is overriden 
  78.         #by set_label otherwise
  79.         img = gtk.Image()
  80.         img.set_from_stock(gtk.STOCK_APPLY,gtk.ICON_SIZE_BUTTON)
  81.         self.button_install.set_image(img)
  82.  
  83.         # setup status
  84.         self.context=self.statusbar_main.get_context_id("context_main_window")
  85.         self.statusbar_main.push(self.context,_("Loading..."))
  86.  
  87.         # setup drag'n'drop
  88.         self.window_main.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
  89.                                        gtk.DEST_DEFAULT_HIGHLIGHT |
  90.                                        gtk.DEST_DEFAULT_DROP,
  91.                                        [('text/uri-list',0,0)],
  92.                                        gtk.gdk.ACTION_COPY)
  93.  
  94.         self.window_main.set_sensitive(False)
  95.         self.notebook_details.set_sensitive(False)
  96.         self.hbox_main.set_sensitive(False)
  97.  
  98.         # show what we have
  99.         self.window_main.show()
  100.         
  101.         self.cprogress = self.CacheProgressAdapter(self.progressbar_cache)
  102.         if not self.openCache():
  103.             self.show_alert(gtk.MESSAGE_ERROR, self.error_header, self.error_body)
  104.             sys.exit(1)
  105.         self.statusbar_main.push(self.context, "")
  106.         
  107.         # setup the details treeview
  108.         self.details_list = gtk.ListStore(gobject.TYPE_STRING)
  109.         column = gtk.TreeViewColumn("")
  110.         render = gtk.CellRendererText()
  111.         column.pack_start(render, True)
  112.         column.add_attribute(render, "markup", 0)
  113.         self.treeview_details.append_column(column)
  114.         self.treeview_details.set_model(self.details_list)
  115.         
  116.         if file != "" and os.path.exists(file):
  117.             self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  118.             while gtk.events_pending(): gtk.main_iteration()        
  119.             self.open(file)
  120.             self.window_main.window.set_cursor(None)
  121.         
  122.         self.window_main.set_sensitive(True)
  123.  
  124.     def _get_file_path_from_dnd_dropped_uri(self, uri):
  125.         """ helper to get a useful path from a drop uri"""
  126.         path = urllib.url2pathname(uri) # escape special chars
  127.         path = path.strip('\r\n\x00') # remove \r\n and NULL
  128.         # get the path to file
  129.         if path.startswith('file:\\\\\\'): # windows
  130.             path = path[8:] # 8 is len('file:///')
  131.         elif path.startswith('file://'): # nautilus, rox
  132.             path = path[7:] # 7 is len('file://')
  133.         elif path.startswith('file:'): # xffm
  134.             path = path[5:] # 5 is len('file:')
  135.         return path
  136.     
  137.     def on_window_main_drag_data_received(self, widget, context, x, y,
  138.                                           selection, target_type, timestamp):
  139.         """ call when we got a drop event """
  140.         uri = selection.data.strip()
  141.         uri_splitted = uri.split() # we may have more than one file dropped
  142.         for uri in uri_splitted:
  143.             path = self._get_file_path_from_dnd_dropped_uri(uri)
  144.             #print 'path to open', path
  145.             if path.endswith(".deb"):
  146.                 self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  147.                 while gtk.events_pending(): gtk.main_iteration()        
  148.                 self.open(path)
  149.                 self.window_main.window.set_cursor(None)
  150.  
  151.     def open(self, file):
  152.         res = GDebiCommon.open(self, file)
  153.         if res == False:
  154.             self.show_alert(gtk.MESSAGE_ERROR, self.error_header, self.error_body)
  155.             return False
  156.             
  157.         self.statusbar_main.push(self.context, "")
  158.  
  159.         # grey in since we are ready for user input now
  160.         self.window_main.set_sensitive(True)
  161.  
  162.         # set window title
  163.         self.window_main.set_title(_("Package Installer - %s") % 
  164.                                    self._deb.pkgName)
  165.  
  166.         # set name
  167.         self.label_name.set_markup(self._deb.pkgName)
  168.         
  169.         self.notebook_details.set_sensitive(True)
  170.         self.hbox_main.set_sensitive(True)
  171.  
  172.  
  173.         # set description
  174.         buf = self.textview_description.get_buffer()
  175.         try:
  176.             long_desc = ""
  177.             raw_desc = string.split(utf8(self._deb["Description"]), "\n")
  178.             # append a newline to the summary in the first line
  179.             summary = raw_desc[0]
  180.             raw_desc[0] = ""
  181.             long_desc = "%s\n" % summary
  182.             for line in raw_desc:
  183.                 tmp = string.strip(line)
  184.                 if tmp == ".":
  185.                     long_desc += "\n"
  186.                 else:
  187.                     long_desc += tmp + "\n"
  188.             #print long_desc
  189.             # do some regular expression magic on the description
  190.             # Add a newline before each bullet
  191.             p = re.compile(r'^(\s|\t)*(\*|0|-)',re.MULTILINE)
  192.             long_desc = p.sub('\n*', long_desc)
  193.             # replace all newlines by spaces
  194.             p = re.compile(r'\n', re.MULTILINE)
  195.             long_desc = p.sub(" ", long_desc)
  196.             # replace all multiple spaces by
  197.             # newlines
  198.             p = re.compile(r'\s\s+', re.MULTILINE)
  199.             long_desc = p.sub("\n", long_desc)
  200.             # write the descr string to the buffer
  201.             buf.set_text(long_desc)
  202.             # tag the first line with a bold font
  203.             tag = buf.create_tag(None, weight=pango.WEIGHT_BOLD)
  204.             iter = buf.get_iter_at_offset(0)
  205.             (start, end) = iter.forward_search("\n",
  206.                                                gtk.TEXT_SEARCH_TEXT_ONLY,
  207.                                                None)
  208.             buf.apply_tag(tag , iter, end)
  209.         except KeyError:
  210.             buf.set_text("No description is available")
  211.  
  212.         # set various status bits
  213.         self.label_version.set_text(self._deb["Version"])
  214.         self.label_maintainer.set_text(utf8(self._deb["Maintainer"]))
  215.         self.label_priority.set_text(self._deb["Priority"])
  216.         self.label_section.set_text(utf8(self._deb["Section"]))
  217.         self.label_size.set_text(self._deb["Installed-Size"] + " KB")
  218.  
  219.         # set filelist
  220.         buf = self.textview_filelist.get_buffer()
  221.         buf.set_text(utf8("\n".join(self._deb.filelist)))
  222.  
  223.         # check the deps
  224.         if not self._deb.checkDeb():
  225.             self.label_status.set_markup("<span foreground=\"red\" weight=\"bold\">"+
  226.                                          _("Error: ") +
  227.                                          self._deb._failureString +
  228.                                          "</span>")
  229.         self.button_install.set_label(_("_Install Package"))
  230.  
  231.             self.button_install.set_sensitive(False)
  232.             self.button_details.hide()
  233.             return
  234.  
  235.         # set version_info_{msg,title} strings
  236.         self.compareDebWithCache()
  237.         self.getChanges()
  238.  
  239.         if self._deb.compareToVersionInCache() == DebPackage.VERSION_SAME:
  240.             self.label_status.set_text(_("Same version is already installed"))
  241.             self.button_install.set_label(_("_Reinstall Package"))
  242.             self.button_install.grab_default()
  243.             self.button_install.set_sensitive(True)
  244.             self.button_details.hide()
  245.             return
  246.  
  247.         if self.version_info_title != "" and self.version_info_msg != "":
  248.             msg = "<big><b>%s</b></big>\n\n%s" % (self.version_info_title,
  249.               self.version_info_msg)
  250.             dialog = gtk.MessageDialog(parent=self.window_main,
  251.                                        flags=gtk.DIALOG_MODAL,
  252.                                        type=gtk.MESSAGE_INFO,
  253.                                        buttons=gtk.BUTTONS_CLOSE)
  254.             dialog.set_markup(msg)
  255.             dialog.run()
  256.             dialog.destroy()
  257.  
  258.         # load changes into (self.install, self.remove, self.unauthenticated)
  259.         if len(self.remove) == len(self.install) == 0:
  260.             self.button_details.hide()
  261.         else:
  262.             self.button_details.show()
  263.             
  264.         self.label_status.set_markup(self.deps)
  265.         img = gtk.Image()
  266.         img.set_from_stock(gtk.STOCK_APPLY,gtk.ICON_SIZE_BUTTON)
  267.         self.button_install.set_image(img)
  268.         self.button_install.set_label(_("_Install Package"))
  269.         self.button_install.set_sensitive(True)
  270.         self.button_install.grab_default()
  271.  
  272.     def on_button_details_clicked(self, widget):
  273.         #print "on_button_details_clicked"
  274.         # sanity check
  275.         if not self._deb:
  276.           return
  277.         self.details_list.clear()
  278.         for rm in self.remove:
  279.             self.details_list.append([_("<b>To be removed: %s</b>") % rm])
  280.         for inst in self.install:
  281.             self.details_list.append([_("To be installed: %s") % inst])
  282.         self.dialog_details.set_transient_for(self.window_main)
  283.         self.dialog_details.run()
  284.         self.dialog_details.hide()
  285.  
  286.     def on_open_activate(self, widget):
  287.         #print "open"
  288.         # build dialog
  289.         fs = gtk.FileChooserDialog(parent=self.window_main,
  290.                                    buttons=(gtk.STOCK_CANCEL, 
  291.                                             gtk.RESPONSE_CANCEL, 
  292.                                             gtk.STOCK_OPEN, 
  293.                                             gtk.RESPONSE_OK),
  294.                                    action=gtk.FILE_CHOOSER_ACTION_OPEN,
  295.                                    title=_("Open Software Package"))
  296.         fs.set_default_response(gtk.RESPONSE_OK)
  297.         # set filter
  298.         filter = gtk.FileFilter()
  299.         filter.add_pattern("*.deb")
  300.         filter.set_name(_("Software packages"))
  301.         #fs.add_filter(filter)
  302.         fs.set_filter(filter)
  303.         # run it!
  304.         if fs.run() == gtk.RESPONSE_OK:
  305.             #print fs.get_filename()
  306.             self.open(fs.get_filename())
  307.         fs.destroy()
  308.  
  309.     def on_refresh_activate(self, widget):
  310.         #print "refresh"
  311.         self.window_main.set_sensitive(False)
  312.         self.openCache()
  313.         if self._deb:
  314.             self.open(self._deb.file)
  315.         self.window_main.set_sensitive(True)
  316.  
  317.     def on_about_activate(self, widget):
  318.         #print "about"
  319.         from Version import VERSION
  320.         self.dialog_about.set_version(VERSION)
  321.         self.dialog_about.run()
  322.         self.dialog_about.hide()
  323.  
  324.     def on_button_install_clicked(self, widget):
  325.         self.install_completed=False
  326.         # check if we actually have a deb, see #213725
  327.         if not self._deb:
  328.             err_header = _("File not found")
  329.             err_body = _("You tried to install a file that does not "
  330.                          "(or no longer) exist. ")
  331.             dia = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR,
  332.                                     gtk.BUTTONS_OK, "")
  333.             dia.set_markup("<b><big>%s</big></b>" % err_header)
  334.             dia.format_secondary_text(err_body)
  335.             dia.run()
  336.             dia.destroy()
  337.             return
  338.         # do it
  339.         self.statusbar_main.push(self.context,_("Installing package file..."))
  340.         if widget != None and len(self.unauthenticated) > 0:
  341.             primary = _("Install unauthenticated software?")
  342.             secondary = _("Malicous software can damage your data "
  343.                           "and take control of your system.\n\n"
  344.                           "The packages below are not authenticated and "
  345.                           "could therefor be of malicous nature.")
  346.             msg = "<big><b>%s</b></big>\n\n%s" % (primary, secondary)
  347.             dialog = gtk.MessageDialog(parent=self.dialog_deb_install,
  348.                                        flags=gtk.DIALOG_MODAL,
  349.                                        type=gtk.MESSAGE_WARNING,
  350.                                        buttons=gtk.BUTTONS_YES_NO)
  351.             dialog.set_markup(msg)
  352.             dialog.set_border_width(6)
  353.             scrolled = gtk.ScrolledWindow()
  354.             textview = gtk.TextView()
  355.             textview.set_cursor_visible(False)
  356.             textview.set_editable(False) 
  357.             buf = textview.get_buffer()
  358.             buf.set_text("\n".join(self.unauthenticated))
  359.             scrolled.add(textview)
  360.             scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  361.             scrolled.show()
  362.             dialog.vbox.pack_start(scrolled)
  363.             textview.show()
  364.             res = dialog.run()
  365.             dialog.destroy()
  366.             if res != gtk.RESPONSE_YES:
  367.                 return
  368.  
  369.         msg_hdr = _("You need to grant administrative rights to install software")
  370.         msg_bdy = _("""
  371. It is a possible security risk to install packages files manually.
  372. Install software from trustworthy software distributors only.
  373. """)
  374.         if os.getuid() != 0:
  375.             os.execl("/usr/bin/gksu", "gksu", "--desktop",
  376.                      "/usr/share/applications/gdebi.desktop",
  377.                      "--message","<big><b>%s</b></big>\n\n%s" % (msg_hdr,msg_bdy),
  378.                      "--always-ask-pass",
  379.                      "--", "gdebi-gtk", "--non-interactive",
  380.                      self._deb.file)
  381.  
  382.         if not self.try_acquire_lock():
  383.             self.statusbar_main.push(self.context,
  384.                                      _("Failed to install package file"))
  385.             self.show_alert(gtk.MESSAGE_ERROR, self.error_header, self.error_body)
  386.             return False
  387.             
  388.         # lock for install
  389.         self.window_main.set_sensitive(False)
  390.         self.button_deb_install_close.set_sensitive(False)
  391.         # clear terminal
  392.         #self._term.feed(str(0x1b)+"[2J")
  393.         self.dialog_deb_install.set_transient_for(self.window_main)
  394.         self.dialog_deb_install.show_all()
  395.  
  396.         if len(self.install) > 0 or len(self.remove) > 0:
  397.             # FIXME: use the new python-apt acquire interface here,
  398.             # or rather use it in the apt module and raise exception
  399.             # when stuff goes wrong!
  400.             if not self.acquire_lock():
  401.               self.show_alert(gtk.MESSAGE_ERROR, self.error_header, self.error_body)
  402.               return False
  403.             fprogress = self.FetchProgressAdapter(self.progressbar_install,
  404.                                                 self.label_action,
  405.                                                 self.dialog_deb_install)
  406.             iprogress = self.InstallProgressAdapter(self.progressbar_install,
  407.                                                     self._term,
  408.                                                     self.label_action,
  409.                                                     self.expander_install)
  410.             errMsg = None
  411.             try:
  412.                 res = self._cache.commit(fprogress,iprogress)
  413.             except IOError, msg:
  414.                 res = False
  415.                 errMsg = "%s" % msg
  416.                 header = _("Could not download all required files")
  417.                 body = _("Please check your internet connection or "
  418.                         "installation medium.")
  419.             except SystemError, msg:
  420.                 res = False
  421.                 header = _("Could not install all dependencies"),
  422.                 body = _("Usually this is related to an error of the "
  423.                         "software distributor. See the terminal window for "
  424.                         "more details.")
  425.             if not res:
  426.                 self.show_alert(gtk.MESSAGE_ERROR, header, body, msg,
  427.                                 parent=self.dialog_deb_install)
  428.                     
  429.                 self.label_install_status.set_markup("<span foreground=\"red\" weight=\"bold\">%s</span>" % header)
  430.                 self.button_deb_install_close.set_sensitive(True)
  431.                 self.button_deb_install_close.grab_default()
  432.                 self.statusbar_main.push(self.context,_("Failed to install package file"))
  433.                 return 
  434.     
  435.         # install the package itself
  436.         self.label_action.set_markup("<b><big>" +
  437.                                      _("Installing %s") % self._deb.pkgName+
  438.                                      "</big></b>")
  439.         dprogress = self.DpkgInstallProgress(self._deb.file,
  440.                                              self.label_install_status,
  441.                                              self.progressbar_install,
  442.                                              self._term,
  443.                                              self.expander_install)
  444.         dprogress.commit()
  445.         self.install_completed=True
  446.         #self.label_action.set_markup("<b><big>"+_("Package installed")+"</big></b>")
  447.         # show the button
  448.         self.button_deb_install_close.set_sensitive(True)
  449.         self.button_deb_install_close.grab_default()
  450.         #Close if checkbox is selected
  451.         if self.checkbutton_autoclose.get_active():
  452.             self.on_button_deb_install_close_clicked(None)
  453.         self.label_action.set_markup("<b><big>"+_("Installation finished")+"</big></b>")
  454.         if dprogress.exitstatus == 0:
  455.             self.label_install_status.set_markup("<i>"+_("Package '%s' was installed") % os.path.basename(self._deb.file)+"</i>")
  456.         else:
  457.             self.label_install_status.set_markup("<b>"+_("Failed to install package '%s'") % os.path.basename(self._deb.file)+"</b>")
  458.             self.expander_install.set_expanded(True)
  459.         self.statusbar_main.push(self.context,_("Installation complete"))
  460.         # FIXME: Doesn't stop notifying
  461.         #self.window_main.set_property("urgency-hint", 1)
  462.  
  463.         # reopen the cache, reread the file
  464.         self.openCache()
  465.         if self._cache._depcache.BrokenCount > 0:
  466.             err_header = _("Failed to completely install all dependencies")
  467.             err_body = _("To fix this run 'sudo apt-get install -f' in a "
  468.                          "terminal window.")
  469.             self.show_alert(gtk.MESSAGE_ERROR, err_header, err_body)
  470.         self.open(self._deb.file)
  471.         
  472.     def on_button_deb_install_close_clicked(self, widget):
  473.         # FIXME: doesn't turn it off
  474.         #self.window_main.set_property("urgency-hint", 0)
  475.         self.dialog_deb_install.hide()
  476.         self.window_main.set_sensitive(True)
  477.     
  478.     def on_checkbutton_autoclose_clicked(self, widget):
  479.         if self.install_completed:
  480.             self.on_button_deb_install_close_clicked(None)            
  481.  
  482.     def on_window_main_delete_event(self, *args):
  483.         if self.window_main.get_property("sensitive"):
  484.             if gtk.main_level() > 0:
  485.                 gtk.main_quit()
  486.             return False
  487.         else: 
  488.             return True
  489.  
  490.     def create_vte(self, arg1,arg2,arg3,arg4):
  491.         #print "create_vte (for the custom glade widget)"
  492.         self._term = vte.Terminal()
  493.         self._term.set_font_from_string("monospace 10")
  494.         return self._term
  495.  
  496.  
  497.     def show_alert(self, type, header, body=None, details=None, parent=None):
  498.         if parent is not None:
  499.              self.dialog_hig.set_transient_for(parent)
  500.         else:
  501.              self.dialog_hig.set_transient_for(self.window_main)
  502.  
  503.         message = "<b><big>%s</big></b>" % header
  504.         if not body == None:
  505.              message = "%s\n\n%s" % (message, body)
  506.         self.label_hig.set_markup(message)
  507.   
  508.         if not details == None:
  509.              buffer = self.textview_hig.get_buffer()
  510.              buffer.set_text(str(details))
  511.              self.expander_hig.set_expanded(False)
  512.              self.expander_hig.show()
  513.              
  514.         if type == gtk.MESSAGE_ERROR:
  515.              self.image_hig.set_property("stock", "gtk-dialog-error")
  516.         elif type == gtk.MESSAGE_WARNING:
  517.              self.image_hig.set_property("stock", "gtk-dialog-warning")
  518.         elif type == gtk.MESSAGE_INFO:
  519.              self.image_hig.set_property("stock", "gtk-dialog-info")
  520.              
  521.         res = self.dialog_hig.run()
  522.         self.dialog_hig.hide()
  523.         if res == gtk.RESPONSE_CLOSE:
  524.             return True
  525.         return False
  526.         
  527.     # embedded classes
  528.     class DpkgInstallProgress(object):
  529.         def __init__(self, debfile, status, progress, term, expander):
  530.             self.debfile = debfile
  531.             self.status = status
  532.             self.progress = progress
  533.             self.term = term
  534.             self.term_expander = expander
  535.             self.time_last_update = time.time()
  536.             self.term_expander.set_expanded(False)
  537.         def commit(self):
  538.             def finish_dpkg(term, pid, status, lock):
  539.                 " helper "
  540.                 self.exitstatus = posix.WEXITSTATUS(status)
  541.                 #print "dpkg finished %s %s" % (pid,status)
  542.                 #print "exit status: %s" % self.exitstatus
  543.                 #print "was signaled %s" % posix.WIFSIGNALED(status)
  544.                 lock.release()
  545.  
  546.             # get a lock
  547.             lock = thread.allocate_lock()
  548.             lock.acquire()
  549.  
  550.             # ui
  551.             self.status.set_markup("<i>"+_("Installing '%s'...") % \
  552.                                    os.path.basename(self.debfile)+"</i>")
  553.             self.progress.pulse()
  554.             self.progress.set_text("")
  555.  
  556.             # prepare reading the pipe
  557.             (readfd, writefd) = os.pipe()
  558.             fcntl.fcntl(readfd, fcntl.F_SETFL,os.O_NONBLOCK)
  559.             #print "fds (%i,%i)" % (readfd,writefd)
  560.  
  561.             # the command
  562.             cmd = "/usr/bin/dpkg"
  563.             argv = [cmd,"--status-fd", "%s"%writefd, "-i", self.debfile]
  564.             env = ["VTE_PTY_KEEP_FD=%s"% writefd,
  565.                    "DEBIAN_FRONTEND=gnome",
  566.                    "APT_LISTCHANGES_FRONTEND=gtk"]
  567.             #print cmd
  568.             #print argv
  569.             #print env
  570.             #print self.term
  571.  
  572.             # prepare for the fork
  573.             reaper = vte.reaper_get()
  574.             signal_id = reaper.connect("child-exited", finish_dpkg, lock)
  575.             pid = self.term.fork_command(command=cmd, argv=argv, envv=env)
  576.             read = ""
  577.             while lock.locked():
  578.                 while True:
  579.                     try:
  580.                         read += os.read(readfd,1)
  581.                     except OSError, (errno,errstr):
  582.                         # resource temporarly unavailable is ignored
  583.                         if errno != 11:
  584.                             print errstr
  585.                         break
  586.                     self.time_last_update = time.time()
  587.                     if read.endswith("\n"):
  588.                         statusl = string.split(read, ":")
  589.                         if len(statusl) < 3:
  590.                             print "got garbage from dpkg: '%s'" % read
  591.                             read = ""
  592.                             break
  593.                         status = statusl[2].strip()
  594.                         #print status
  595.                         if status == "error" or status == "conffile-prompt":
  596.                             self.term_expander.set_expanded(True)
  597.                         read = ""
  598.                 self.progress.pulse()
  599.                 while gtk.events_pending():
  600.                     gtk.main_iteration()
  601.                 time.sleep(0.2)
  602.                 # if the terminal has not reacted for some time, do something
  603.                 if (not self.term_expander.get_expanded() and 
  604.                     (self.time_last_update + GDEBI_TERMINAL_TIMEOUT) < time.time()):
  605.                   self.term_expander.set_expanded(True)
  606.             self.progress.set_fraction(1.0)
  607.             reaper.disconnect(signal_id)
  608.     
  609.     class InstallProgressAdapter(InstallProgress):
  610.         def __init__(self,progress,term,label,term_expander):
  611.             InstallProgress.__init__(self)
  612.             self.progress = progress
  613.             self.term = term
  614.             self.term_expander = term_expander
  615.             self.finished = False
  616.             self.action = label
  617.             self.time_last_update = time.time()
  618.             reaper = vte.reaper_get()
  619.             reaper.connect("child-exited",self.child_exited)
  620.             self.env = ["VTE_PTY_KEEP_FD=%s"% self.writefd,
  621.                         "DEBIAN_FRONTEND=gnome",
  622.                         "APT_LISTCHANGES_FRONTEND=gtk"]
  623.         def child_exited(self,term, pid, status):
  624.             #print "child_exited: %s %s %s %s" % (self,term,pid,status)
  625.             self.apt_status = posix.WEXITSTATUS(status)
  626.             self.finished = True
  627.         def error(self, pkg, errormsg):
  628.             # FIXME: display a msg
  629.             self.term_expander.set_expanded(True)
  630.         def conffile(self, current, new):
  631.             # FIXME: display a msg or expand term
  632.             self.term_expander.set_expanded(True)
  633.         def startUpdate(self):
  634.             #print "startUpdate"
  635.             apt_pkg.PkgSystemUnLock()
  636.             self.action.set_markup("<i>"+_("Installing dependencies...")+"</i>")
  637.             self.progress.set_fraction(0.0)
  638.             self.progress.set_text("")
  639.         def statusChange(self, pkg, percent, status):
  640.             self.progress.set_fraction(percent/100.0)
  641.             self.progress.set_text(status)
  642.             self.time_last_update = time.time()
  643.         def updateInterface(self):
  644.             InstallProgress.updateInterface(self)
  645.             while gtk.events_pending():
  646.                 gtk.main_iteration()
  647.             if (not self.term_expander.get_expanded() and 
  648.                 (self.time_last_update + GDEBI_TERMINAL_TIMEOUT) < time.time()):
  649.               self.term_expander.set_expanded(True)
  650.         def fork(self):
  651.             return self.term.forkpty(envv=self.env)
  652.         def waitChild(self):
  653.             while not self.finished:
  654.                 self.updateInterface()
  655.             return self.apt_status
  656.         
  657.     class FetchProgressAdapter(apt.progress.FetchProgress):
  658.         def __init__(self,progress,action,main):
  659.             #print "FetchProgressAdapter.__init__()"
  660.             self.progress = progress
  661.             self.action = action
  662.             self.main = main
  663.         def start(self):
  664.             #print "start()"
  665.             self.action.set_markup("<i>"+_("Downloading additional package files...")+"</i>")
  666.             self.progress.set_fraction(0)
  667.         def stop(self):
  668.             #print "stop()"
  669.             pass
  670.         def pulse(self):
  671.             at_item = min(self.currentItems + 1, self.totalItems)
  672.             if self.currentCPS > 0:
  673.                 self.progress.set_text(_("File %s of %s at %sB/s") % (at_item,self.totalItems,apt_pkg.SizeToStr(self.currentCPS)))
  674.             else:
  675.                 self.progress.set_text(_("File %s of %s") % (at_item,self.totalItems))
  676.             self.progress.set_fraction(self.currentBytes/self.totalBytes)
  677.             while gtk.events_pending():
  678.                 gtk.main_iteration()
  679.             return True
  680.         def mediaChange(self, medium, drive):
  681.             #print "mediaChange %s %s" % (medium, drive)
  682.             msg = _("Please insert '%s' into the drive '%s'" % (medium,drive))
  683.             dialog = gtk.MessageDialog(parent=self.main,
  684.                                        flags=gtk.DIALOG_MODAL,
  685.                                        type=gtk.MESSAGE_QUESTION,
  686.                                        buttons=gtk.BUTTONS_OK_CANCEL)
  687.             dialog.set_markup(msg)
  688.             res = dialog.run()
  689.             #print res
  690.             dialog.destroy()
  691.             if  res == gtk.RESPONSE_OK:
  692.                 return True
  693.             return False
  694.  
  695.     class CacheProgressAdapter(apt.progress.FetchProgress):
  696.         def __init__(self, progressbar):
  697.             self.progressbar = progressbar
  698.         def update(self, percent):
  699.             self.progressbar.show()
  700.             self.progressbar.set_fraction(percent/100.0)
  701.             #self.progressbar.set_text(self.op)
  702.             while gtk.events_pending():
  703.                 gtk.main_iteration()
  704.         def done(self):
  705.             self.progressbar.hide()
  706.         
  707. if __name__ == "__main__":
  708.     app = GDebi("data/",None)
  709.  
  710.     pkgs = ["3ddesktop"]
  711.     for pkg in pkgs:
  712.         print "installing %s" % pkg
  713.         app._cache[pkg].markInstall()
  714.  
  715.     for pkg in app._cache:
  716.         if pkg.markedInstall or pkg.markedUpgrade:
  717.             print pkg.name
  718.  
  719.     apt_pkg.PkgSystemLock()
  720.     app.dialog_deb_install.set_transient_for(app.window_main)
  721.     app.dialog_deb_install.show_all()
  722.  
  723.     # install the dependecnies
  724.     fprogress = app.FetchProgressAdapter(app.progressbar_install)
  725.     iprogress = app.InstallProgressAdapter(app.progressbar_install, app._term)
  726.     res = app._cache.commit(fprogress,iprogress)
  727.     print "commit retured: %s" % res
  728.     
  729.